﻿using iTool.Clustering.Center.Membership;
using iTool.Cloud.Center.Model;
using iTool.Clustering.Center.ServiceProvider;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Orleans;
using Orleans.Configuration;
using Orleans.Hosting;
using Orleans.Providers;
using Orleans.Runtime;
using Orleans.Runtime.MembershipService;
using Orleans.Storage;
using Orleans.Transactions.Abstractions;
using System;
using iTool.Cloud.Center;
using iTool.Common;

namespace iTool.Clustering.Center
{
    public static class iToolGatewayServerExtensions
    {
        static bool isSetConfigureOptions = false;
        public static ISiloHostBuilder SetiCenterClustering(this ISiloHostBuilder builder, ClusteringStaticOptions configureOptions)
        {
            iPrint.Line("\niTool> Connectioning iToolCenter...");

            if (builder == null) throw new ArgumentNullException(nameof(builder));
            isSetConfigureOptions = true;

            var clientBuilder = new ClientBuilder();

            clientBuilder.Configure<ClusterOptions>(options =>
            {
                options.ClusterId = configureOptions.ClusterOptions.ClusterId;
                options.ServiceId = configureOptions.ClusterOptions.ServiceId;
            });

            clientBuilder.UseStaticClustering(options =>
            {
                options.Gateways = configureOptions.GetGatewayUris();
            });

            clientBuilder.ConfigureApplicationParts(parts => 
                parts.AddApplicationPart(typeof(ClusterClientService).Assembly)
                .WithCodeGeneration()
                .WithReferences());

            var client = clientBuilder.Build();
            client.Connect().Wait();

            iPrint.Success("\niTool> Connectioned successfully.");

            // 避免对象冲突
            iCenterClusterClient clusterClient = new CenterClusterClient(client);
            builder.ConfigureServices(services => services.AddSingleton(clusterClient));

            return builder;
        }

        static void CheckSetConfigure()
        {
            if (!isSetConfigureOptions) throw new Exception("please invoke SetiCenterClustering");
        }

        // Clustering

        public static ISiloHostBuilder UseiCenterClustering(this ISiloHostBuilder builder)
        {
            CheckSetConfigure();
            return builder.ConfigureServices(services => services.AddSingleton<IMembershipTable, iCenterClusteringProvider>());
        }

        // Reminders
        public static ISiloHostBuilder UseiCenterReminders(this ISiloHostBuilder builder)
        {
            CheckSetConfigure();
            return builder.ConfigureServices(services => services.AddSingleton<IReminderTable, iCenterReminderProvider>());
        }

        // Storage
        public static ISiloHostBuilder UseiCenterStorage(this ISiloHostBuilder builder)
        {
            return builder.UseiCenterStorage(ProviderConstants.DEFAULT_STORAGE_PROVIDER_NAME);
        }

        public static ISiloHostBuilder UseiCenterStorage(this ISiloHostBuilder builder, string name)
        {
            return builder.ConfigureServices(services =>
            {
                services.TryAddSingleton(sp => sp.GetServiceByName<IGrainStorage>(ProviderConstants.DEFAULT_STORAGE_PROVIDER_NAME));
                services.AddSingletonNamedService(name, Create<iCenterStorageProvider>);
                services.AddSingletonNamedService(name, (s, n) => (ILifecycleParticipant<ISiloLifecycle>)s.GetRequiredServiceByName<IGrainStorage>(n));
            });
        }

        // Transactional
        public static ISiloHostBuilder UseiCenterTransactional(this ISiloHostBuilder builder)
        {
            builder.UseTransactions();
            return builder.UseiCenterTransactional(ProviderConstants.DEFAULT_STORAGE_PROVIDER_NAME);
        }

        public static ISiloHostBuilder UseiCenterTransactional(this ISiloHostBuilder builder, string name)
        {
            return builder.ConfigureServices(services => services.UseiCenterTransactional(name));
        }

        private static IServiceCollection UseiCenterTransactional(this IServiceCollection services, string name)
        {
            services.TryAddSingleton<ITransactionalStateStorageFactory>(sp => sp.GetServiceByName<ITransactionalStateStorageFactory>(ProviderConstants.DEFAULT_STORAGE_PROVIDER_NAME));
            services.AddSingletonNamedService<ITransactionalStateStorageFactory>(name, iCenterTransactionalStateStorageFactory.Create);
            return services;
        }

        static IGrainStorage Create<TStorage>(IServiceProvider services, string providerName)
            where TStorage : class, IGrainStorage
        {
            return ActivatorUtilities.CreateInstance<TStorage>(services, providerName);
        }
    }
}
